package cn.thens.kdroid.core.util

import android.os.Handler
import android.os.Looper
import android.os.Message
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

@Suppress("unused", "MemberVisibilityCanBePrivate")
interface Scheduler {
    fun schedule(run: () -> Unit)
    fun schedule(delay: Long, unit: TimeUnit, run: () -> Unit)

    companion object {
        val main: Scheduler by lazy {
            from(Looper.getMainLooper())
        }

        val single: Scheduler by lazy {
            from(Executors.newSingleThreadExecutor())
        }

        val io: Scheduler by lazy {
            from(Executors.newCachedThreadPool())
        }

        val computation: Scheduler  by lazy {
            from(Executors.newScheduledThreadPool(coreCount))
        }

        fun from(executor: Executor): Scheduler {
            return ExecutorScheduler(executor)
        }

        fun from(looper: Looper): Scheduler {
            val thread = looper.thread
            if (thread.state == Thread.State.NEW) thread.start()
            return HandlerScheduler(looper)
        }

        private fun helper(): Scheduler = computation

        private val coreCount: Int by lazy { Runtime.getRuntime().availableProcessors() }

    }

    private class ExecutorScheduler(private val executor: Executor) : Scheduler {
        override fun schedule(delay: Long, unit: TimeUnit, run: () -> Unit) {
            when {
                delay <= 0L -> executor.execute(run)
                executor is ScheduledExecutorService -> executor.schedule(run, delay, unit)
                else -> helper().schedule(delay, unit) { schedule(run) }
            }
        }

        override fun schedule(run: () -> Unit) {
            schedule(0, TimeUnit.NANOSECONDS, run)
        }
    }

    private class HandlerScheduler(private val looper: Looper) : Scheduler {
        val handler = object : Handler(looper) {
            override fun handleMessage(msg: Message?) {
                if (msg == null) return
                msg.callback.run()
            }
        }

        override fun schedule(run: () -> Unit) {
            when (looper) {
                Looper.myLooper() -> run()
                else -> handler.post(run)
            }
        }

        override fun schedule(delay: Long, unit: TimeUnit, run: () -> Unit) {
            when {
                delay <= 0L -> schedule(run)
                else -> handler.postDelayed(run, unit.toMillis(delay))
            }
        }
    }
}